﻿using System;
using System.Diagnostics;
using System.IO;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Files;

namespace Microsoft.WindowsAzure.Accelerator.Synchronization
{
    /// <summary>
    /// Primary class for creating and performing blob to local file system synchronization.
    /// </summary>
    internal class CloudSync : ISyncProvider
    {
        /// <summary>
        /// Gets or sets the cloud storage account.
        /// </summary>
        /// <value>The cloud storage account.</value>
        public CloudStorageAccount Account { get; set; }

        /// <summary>
        /// Gets or sets the local path.
        /// </summary>
        /// <value>The local path.</value>
        public String LocalPath { get; set; }

        /// <summary>
        /// Gets or sets the name of the container.
        /// </summary>
        /// <value>The name of the container.</value>
        public String ContainerName { get; set; }

        /// <summary>
        /// Gets or sets the sync direction and order (when bi-directional sync).
        /// </summary>
        /// <value>The sync direction.</value>
        public SyncDirectionOrder SyncDirection { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="CloudSync"/> class.
        /// </summary>
        public CloudSync()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CloudSync"/> class.
        /// </summary>
        /// <param name="account">The account.</param>
        /// <param name="containerName">Name of the container.</param>
        /// <param name="localPath">The local path.</param>
        /// <param name="syncDirections">The sync directions.</param>
        public CloudSync(CloudStorageAccount account, String containerName, String localPath, SyncDirectionOrder syncDirections)
        {
            ContainerName = containerName;
            Account = account;
            LocalPath = localPath;
            SyncDirection = syncDirections;
        }

        /// <summary>
        /// Starts the cloud blob to local file system synchronization.
        /// </summary>
        public void StartSync()
        {
            if (String.IsNullOrEmpty(LocalPath))
                throw new ArgumentException("CloudSync| A valid local path is required.");
            if (String.IsNullOrEmpty(ContainerName))
                throw new ArgumentException("CloudSync| A valid blob storage container name is required.");
            if (Account == null)
                throw new ArgumentException("CloudSync| A valid cloud storage account is required.");

            if (!Directory.Exists(LocalPath))
            {
                Directory.CreateDirectory(LocalPath);
                if (!Directory.Exists(LocalPath))
                    throw new ArgumentException("CloudSync| Unable to create local physical directory.");
            }

            CloudSyncStore blobStore = new CloudSyncStore(ContainerName, Account);
            CloudSyncProvider azureProvider = new CloudSyncProvider(ContainerName, blobStore);
            azureProvider.ApplyingChange += UploadingFile;

            FileSyncProvider fileSyncProvider = null;
            try
            {
                fileSyncProvider = new FileSyncProvider(LocalPath);
            }
            catch (ArgumentException)
            {
                fileSyncProvider = new FileSyncProvider(Guid.NewGuid(), LocalPath);
            }
            fileSyncProvider.ApplyingChange += DownloadingFile;

            try
            {
                SyncOrchestrator orchestrator = new SyncOrchestrator
                                                    {
                                                        LocalProvider = fileSyncProvider,
                                                        RemoteProvider = azureProvider,
                                                        Direction = SyncDirection
                                                    };
                orchestrator.Synchronize();
            }
            catch (Exception ex)
            {
                Trace.TraceError("CloudSync| Synchronization Failed: {0}", ex.Message);
            }
            finally
            {
                fileSyncProvider.Dispose();
            }

            Trace.TraceInformation("CloudSync| Synchronization Complete.");
        }

        /// <summary>
        /// Tracing information for file download events.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The <see cref="Microsoft.Synchronization.Files.ApplyingChangeEventArgs"/> instance containing the event data.</param>
        public static void DownloadingFile(Object sender, ApplyingChangeEventArgs args)
        {
            switch (args.ChangeType)
            {
                case ChangeType.Create: Trace.TraceInformation("CloudSync| Creating File: {0}...", args.NewFileData.Name); break;
                case ChangeType.Delete: Trace.TraceInformation("CloudSync| Deleting File: {0}...", args.CurrentFileData.Name); break;
                case ChangeType.Rename: Trace.TraceInformation("CloudSync| Renaming File: {0} to {1}...", args.CurrentFileData.Name, args.NewFileData.Name); break;
                case ChangeType.Update: Trace.TraceInformation("CloudSync| Updating File: {0}...", args.NewFileData.Name);break;
            }
        }

        /// <summary>
        /// Tracing information for file upload events.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The <see cref="CloudSyncEventArgs"/> instance containing the event data.</param>
        public static void UploadingFile(Object sender, CloudSyncEventArgs args)
        {
            switch (args.ChangeType)
            {
                case ChangeType.Create: Trace.TraceInformation("CloudSync| Creating Azure Blob: {0}...", args.CurrentBlobName); break;
                case ChangeType.Delete: Trace.TraceInformation("CloudSync| Deleting Azure Blob: {0}...", args.CurrentBlobName); break;
                case ChangeType.Rename: Trace.TraceInformation("CloudSync| Renaming Azure Blob: {0} to {1}...", args.CurrentBlobName, args.NewBlobName); break;
                case ChangeType.Update: Trace.TraceInformation("CloudSync| Updating Azure Blob: {0}...", args.CurrentBlobName); break;
            }
        }
    }
}